package sample.algo.sistr.algosample.algos.arrays;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import java.security.InvalidParameterException;
import java.util.Arrays;

import sample.algo.sistr.algosample.R;
import sample.algo.sistr.algosample.algos.Algo;

public class Algo00 extends Algo {

    private TextView textViewTestResult;

    private EditText editTextA;
    private EditText editTextB;

    public Algo00(Context context) {
        super("Separate entry sorting", context);
        textViewTestResult = null;
    }

    @Override
    public View GetDisplay() {
        ViewGroup root = (ViewGroup)((LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.algo_array_00, null);

        textViewTestResult = (TextView)root.findViewById(R.id.textViewTest);

        Button buttonTest = (Button)root.findViewById(R.id.buttonTest);
        buttonTest.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                StringBuilder testResult = new StringBuilder();

                // test on well formed input
                int i = 0;
                runTest(i++, new int[] {1, 8, 9, 11, 22, 0, 0, 0, 0, 0}, new int[] {0, 7, 10, 12, 21}, new int[]{0, 1, 7, 8, 9, 10, 11, 12, 21, 22}, testResult);
                runTest(i++, new int[] {0, 0}, new int[] {1}, new int[]{0, 1}, testResult);
                runTest(i++, new int[] {1, 2, 0, 0}, new int[] {3, 4}, new int[]{1, 2, 3, 4}, testResult);
                runTest(i++, new int[] {3, 4, 0, 0}, new int[] {1, 2}, new int[]{1, 2, 3, 4}, testResult);
                runTest(i++, new int[] {0, 0, 1, 2}, new int[] {0, 0}, new int[]{0, 0, 0, 0}, testResult);
                runTest(i++, new int[] {1}, new int[] {}, new int[]{1}, testResult);

                // test on invalid input
                runInvalidTest(i++, null, null, testResult);
                runInvalidTest(i++, new int[] {1, 2}, null, testResult);
                runInvalidTest(i++, null, new int[] {1, 2}, testResult);
                runInvalidTest(i++, new int[] {1, 2}, new int[] {}, testResult);
                runInvalidTest(i++, new int[] {1, 2}, new int[] {1, 2, 3}, testResult);

                if(textViewTestResult != null)
                    textViewTestResult.setText(testResult);
            }
        });

        editTextA = (EditText)root.findViewById(R.id.editTextA);
        editTextA.setText("[1, 3, 9, 10, 0, 0, 0, 0]");
        editTextB = (EditText)root.findViewById(R.id.editTextB);
        editTextB.setText("[0, 5, 18, 20]");

        Button buttonRun = (Button)root.findViewById(R.id.buttonRun);
        buttonRun.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                StringBuilder result = new StringBuilder();


                String stringA = editTextA.getText().toString();
                stringA = stringA.replaceAll("\\s+","");
                String stringB = editTextB.getText().toString();
                stringB = stringB.replaceAll("\\s+","");

                String[] intsA = stringA.replaceAll("\\[", "").replaceAll("\\]", "").split(",");
                String[] intsB = stringB.replaceAll("\\[", "").replaceAll("\\]", "").split(",");

                int[] A = new int[intsA.length];
                int[] B = new int[intsB.length];

                for (int i = 0; i < intsA.length; i++) {
                    try {
                        A[i] = Integer.parseInt(intsA[i]);
                    } catch (NumberFormatException nfe) {};
                }

                for (int i = 0; i < intsB.length; i++) {
                    try {
                        B[i] = Integer.parseInt(intsB[i]);
                    } catch (NumberFormatException nfe) {};
                }

                try {
                    result.append("input A: ").append(Arrays.toString(A)).append("\n");
                    result.append("input B: ").append(Arrays.toString(B)).append("\n");
                    function(A, B);
                    result.append("Result: ").append(Arrays.toString(A)).append("\n");
                } catch(InvalidParameterException e) {
                    result.append("InvalidParameterException : ").append(e.getMessage());
                }

                if(textViewTestResult != null)
                    textViewTestResult.setText(result);

            }
        });

        return root;
    }

    /**
     * A is of size 2*n and B of size n.
     * A contain n integers sorted in its first half.
     * B contain n integers sorted.
     * function will fill A with integers from B and sort A.
     * @param A integer array of size 2*n
     * @param B integer array of size n
     */

    private void function(int A[], int B[]) {
        if(A == null || B == null)
            throw new InvalidParameterException("A and B must not be null.");

        if(A.length != B.length*2 && !(A.length == 1 && B.length == 0))
            throw new InvalidParameterException("Length or argument A must be twice the one of argument B.");


        // We start iterating fro; the highest value in A and B.
        // We copy the highest value is in A in it's final place and decrement its container pointer.

        int currentIndexA = B.length - 1;
        int currentIndexB = B.length - 1;

        int currentFillingIndex = A.length - 1;

        while(currentIndexA >= 0 && currentIndexB >= 0)
            A[currentFillingIndex--] = (A[currentIndexA]>B[currentIndexB])?A[currentIndexA--]:B[currentIndexB--];

        // if currentB index doesn't reach the start we copy the final B value in A
        if(currentIndexB >= 0)
            System.arraycopy(B, 0, A, 0, currentIndexB + 1);

        // A is now sorted
    }

    private boolean arraysEquals(int A[], int B[]) {
        if(A.length != B.length)
            return false;

        for(int i = 0 ; i < A.length ; i++)
            if(A[i] != B[i])
                return false;

        return true;
    }

    private boolean runTest(int testid, int A[], int B[], int R[], StringBuilder out_res) {
        function(A, B);
        if(arraysEquals(A, R)) {
            out_res.append("test[").append(testid).append("] SUCCESS\n");
            return true;
        }
        else {
            out_res.append("test[").append(testid).append("] FAIL\n");
            out_res.append("Output is ").append(Arrays.toString(A)).append("\nExpected is : ").append(Arrays.toString(R)).append("\n");
            return false;
        }
    }

    private boolean runInvalidTest(int testid, int A[], int B[], StringBuilder out_res) {
        try {
            function(A, B);
        } catch(InvalidParameterException e) {
            out_res.append("test[").append(testid).append("] SUCCESS\n");
            return true;
        } catch(Exception e) {
            out_res.append("test[").append(testid).append("] FAIL\n");
            out_res.append(e.getClass().getCanonicalName()).append(" : ").append(e.getMessage()).append("\n");
            return false;
        } catch(Error e) {
            out_res.append("test[").append(testid).append("] FAIL\n");
            out_res.append(e.getClass().getCanonicalName()).append(" : ").append(e.getMessage()).append("\n");
            return false;
        }
        out_res.append("test[").append(testid).append("] FAIL\n");
        return false;
    }



}
